package org.iblink.gui.swing.manager.impl;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.swing.DropMode;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JToolBar;
import javax.swing.ListCellRenderer;
import javax.swing.TransferHandler;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.filechooser.FileSystemView;

import org.iblink.core.domain.model.Asset;
import org.iblink.core.domain.model.Category;
import org.iblink.core.service.AssetService;
import org.iblink.gui.swing.actions.AssetAction;
import org.iblink.gui.swing.dnd.transferable.TransferableAssetList;
import org.iblink.gui.swing.manager.AssetBrowserManager;
import org.iblink.gui.swing.views.AssetBrowser;

public class AssetBrowserManagerImpl extends TransferHandler implements AssetBrowserManager {
  
  /**
   * Generated serial version UID
   */
  private static final long serialVersionUID = 6141326179431777388L;

  private AssetBrowser assetBrowser;
  
  private AssetService assetService;
  
  private Category category;
  
  private JList assetList;
  
  @Override
  public void initAssetBrowser(AssetBrowser browser) {
    this.assetBrowser = browser;
    
    AssetAction.getAddAssetAction().addActionListener(this);
    AssetAction.getDelAssetAction().addActionListener(this);
    AssetAction.getOpenAssetAction().addActionListener(this);
    AssetAction.getEditAssetAction().addActionListener(this);
  }

  @Override
  public void categorySelected(Category category) {
    if (category != null) {
      this.category = category;
      
      assetBrowser.setBorder(new TitledBorder("Asset Browser for category \"" + category.getName() + "\""));
      
      refreshContent();
    }
  }
  
  private void refreshContent() {
    assetBrowser.removeAll();
    assetBrowser.setLayout(new BorderLayout());
    
    // add tool bar for asset browser:
    assetBrowser.add(buildMenuBar(), BorderLayout.SOUTH);
    
    List<Asset> assets = assetService.getAssetsForCategory(category);
    if (assets == null || assets.isEmpty()) {
      assetList = null;
      JLabel label = new JLabel("This category has no assets assigned to it.", JLabel.CENTER);
      assetBrowser.add(label);
    } else {
      assetList = new JList(assets.toArray());
      assetList.setCellRenderer(new ListCellRenderer() {
        @Override
        public Component getListCellRendererComponent(JList list, Object value,
            int index, boolean isSelected, boolean cellHasFocus) {
          Asset asset = (Asset) value;
          File assetFile = new File(asset.getPath());
          
          JLabel cell = new JLabel(asset.toString());
          if (assetFile.exists()) {
            FileSystemView view = FileSystemView.getFileSystemView();      
            Icon icon = view.getSystemIcon(assetFile);
            cell.setIcon(icon);
            if (isSelected) {
              cell.setOpaque(true);
              cell.setBackground(Color.lightGray);
            }
          } else {
            cell.setFont(new Font(null, Font.ITALIC, 12));
            cell.setOpaque(true);
            if(isSelected) {
              cell.setBackground(Color.lightGray);
            } else {
              cell.setBackground(Color.red);
            }
          }
          return cell;
        }
      });
      assetList.addListSelectionListener(this);

      assetList.setDragEnabled(true);
      assetList.setTransferHandler(this);
      assetList.setDropMode(DropMode.ON);
      
      assetBrowser.add(assetList);
    }
    assetBrowser.validate();
    assetBrowser.getParent().repaint();
    
    updateActions();
  }
  
  private JToolBar buildMenuBar() {
    JToolBar toolBar = new JToolBar();
    
    JButton button = new JButton(AssetAction.getAddAssetAction());
    toolBar.add(button);
    button = new JButton(AssetAction.getDelAssetAction());
    toolBar.add(button);
    button = new JButton(AssetAction.getOpenAssetAction());
    toolBar.add(button);
    button = new JButton(AssetAction.getEditAssetAction());
    toolBar.add(button);
    
    toolBar.setPreferredSize(new Dimension(50, 25));
    
    return toolBar;
  }

  /**
   * @return the assetService
   */
  public AssetService getAssetService() {
    return assetService;
  }

  /**
   * @param assetService the assetService to set
   */
  public void setAssetService(AssetService assetService) {
    this.assetService = assetService;
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    //Handle Add asset action
    if (AssetAction.ADDACTION.equals(e.getActionCommand())) {
    //Create a file chooser
      final JFileChooser fc = new JFileChooser();
      
      //In response to a button click:
      if (fc.showOpenDialog(assetBrowser) == JFileChooser.APPROVE_OPTION) {
        File file = fc.getSelectedFile();
        
         addAssetFromFile(file);
        
        // refresh list display
        refreshContent();
      }
    // Handle Delete asset(s) action
    } else if (AssetAction.DELETEACTION.equals(e.getActionCommand())) {
      // for each selected asset
      for(int i = 0; i < assetList.getSelectedValues().length; i++) {
        Asset asset = (Asset) assetList.getSelectedValues()[i];
        // remove category
        asset.removeCategory(category);
        if (asset.getCategories().isEmpty()) {
          // this asset is not assigned to any category, delete it
          assetService.delete(asset);
        } else {
          // this asset is still assigned to a category,
          // persist the fact that the category was removed
          assetService.save(asset);
        }
      }
      
      // refresh list display
      refreshContent();
    // Handle Open action
    } else if (AssetAction.OPENACTION.equals(e.getActionCommand())) {
      Asset asset = (Asset) assetList.getSelectedValue();
      try {
        // use system open program
        Desktop.getDesktop().open(new File(asset.getPath()));
      } catch (IOException e1) {
        e1.printStackTrace();
      }
    // Handle Edit action
    } else if (AssetAction.EDITACTION.equals(e.getActionCommand())) {
      Asset asset = (Asset) assetList.getSelectedValue();
      try {
        // use system edit program
        Desktop.getDesktop().edit(new File(asset.getPath()));
      } catch (IOException e1) {
        e1.printStackTrace();
      }
    }
  }
  
  private void addAssetFromFile(File file) {
    Asset newAsset = assetService.findByPath(file.getPath());
    if (newAsset == null) {
      // create asset from chosen file
      newAsset = new Asset();
      newAsset.setName(file.getName());
      newAsset.setPath(file.getPath());
    }
    
    // assign category to the asset
    newAsset.addCategory(category);
    
    // persist asset
    assetService.save(newAsset);
  }

  @Override
  public void valueChanged(ListSelectionEvent e) {
    // Enable or disable actions depending on selection(s)
    updateActions();
  }
  
  private void updateActions() {
    // only one selection for open and edit actions
    // also depends on file existence
    boolean enabled = assetList != null && assetList.getSelectedValues().length == 1 &&
                      new File(((Asset)assetList.getSelectedValue()).getPath()).exists();
    AssetAction.getOpenAssetAction().setEnabled(enabled);
    AssetAction.getEditAssetAction().setEnabled(enabled);
    // there must be at least one selected list item to delete
    AssetAction.getDelAssetAction().setEnabled(assetList != null && assetList.getSelectedValues().length != 0);
    // add action is always enabled
    AssetAction.getAddAssetAction().setEnabled(category != null);
  }

  @Override
  public void importFiles(List<File> files, Category category) {
    if (category == null || !category.equals(this.category)) {
      return;
    }
    
    // import files as assets
    for (Iterator<File> iter = files.iterator(); iter.hasNext();) {
      addAssetFromFile(iter.next());
    }
    refreshContent();
  }

  @Override
  public void importAssets(List<Asset> assets, Category category) {
    for (Iterator<Asset> iter = assets.iterator(); iter.hasNext();) {
      Asset asset = iter.next();
      asset.addCategory(category);
      assetService.save(asset);
    }
    
    if (category != null && category.equals(this.category)) {
      refreshContent();
    }
  }
  
  public int getSourceActions(JComponent c) {
    //only allow move action
    return MOVE;
  }
  
  public Transferable createTransferable(JComponent c) {
    if (! (c instanceof JList) || assetList == null) {
      return null;
    }
    // Get the list of selected assets
    List<Asset> assets = new ArrayList<Asset>();
    for (int i = 0; i < assetList.getSelectedValues().length; i++) {
      Asset asset = (Asset) assetList.getSelectedValues()[i];
      assets.add(asset);
    }
    
    return new TransferableAssetList(assets);
  }
  
  public void exportDone(JComponent c, Transferable t, int action) {
  }
  
  public boolean canImport(TransferSupport supp) {
    return false;
  }
  
  public boolean importData(TransferSupport supp) {
    if (!canImport(supp) || !supp.isDrop()) {
      return false;
    }
    return true;
  }

  @Override
  public boolean beforeCategoryDeleted(Category category) {
    // TODO ask user before deletion?
    
    // also delete assets
    List<Asset> assets = assetService.getAssetsForCategory(category);
    for(Iterator<Asset> iter = assets.iterator(); iter.hasNext();) {
      Asset asset = iter.next();
      // remove category
      asset.removeCategory(category);
      if (asset.getCategories().isEmpty()) {
        // this asset is not assigned to any category, delete it
        assetService.delete(asset);
      } else {
        // this asset is still assigned to a category,
        // persist the fact that the category was removed
        assetService.save(asset);
      }
    }
    
    return true;
  }
}
